package ru.xeden.core.common;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class ObjectRegistr<T>
{
    private final Logger LOGGER = LoggerFactory.getLogger(ObjectRegistr.class);
    private List<T> registerList = new ArrayList<T>();
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    private Comparator<T> comparator;
    private String registerName;

    public ObjectRegistr()
    {
        this("Object register");
    }

    public ObjectRegistr(String registrName)
    {
        this.registerName = registrName;
    }
    
    /**
     * Интерфейс для применения каких либо действий к объектам в регистре
     * @author evga
     *
     */
    public interface IAcceptor<T>
    {
        /**
         * @param object некий объект в регистре 
         * @return true если нужно перебирать объекты еще
         */
        boolean accept(T object);
    }

    public void setComparator(Comparator<T> comparator)
    {
        this.comparator = comparator;
    }
    
    public String getRegisterName()
    {
        return registerName;
    }

    /**
     * Регистрирует объект
     * @param factory
     */
    public boolean register(T object)
    {
        writeLock.lock();
        try
        {
            if (!registerList.contains(object))
            {
                registerList.add(object);
                if (comparator != null)
                {
                    Collections.sort(registerList, comparator);
                }
                LOGGER.debug(registerName +": Объект '" + object + "' зарегестрирован");
                return true;
            }
            else
            {
                LOGGER.error(registerName + ": Объект '" + object + "' уже зарегестрирован");
                return false;
            }
        }
        finally
        {
            writeLock.unlock();
        }
    }

    /**
     * Удаляет объект из регистра
     * @param provider
     */
    public boolean unregister(T object)
    {
        writeLock.lock();
        try
        {
            if (registerList.remove(object))
            {
                LOGGER.debug(registerName + ": Объект '" + object + "' удален с регистра");
                return true;
            }
            else
            {
                LOGGER.warn(registerName + ": Попытка удалить объект с регистра '" + object
                    + "' но он не зарегестрирован");
                return false;
            }
        }
        finally
        {
            writeLock.unlock();
        }
    }

    public List<T> getRegisteredObjects()
    {
        return Collections.unmodifiableList(registerList);
    }
    
    public void accept(IAcceptor<T> acceptor)
    {
        readLock.lock();
        try
        {
            for (T object : registerList)
            {
                if (!acceptor.accept(object))
                {
                    break;
                }
            }
        }
        finally
        {
            readLock.unlock();
        }
    }
}
